package com.im.core.proto.util;

import com.google.protobuf.Descriptors;
import com.google.protobuf.DynamicMessage;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p></p>
 *
 * @author GaoShuangWen
 * @since 2020-04-30 21:27
 */
@Slf4j
public class ConvertUtil {

    /**
     *  该方法将javabean对象转换成protobuf对应的bean
     *
     *  @param javaBean 普通的javabean
     *  @param protoBuilder protobuf对象的builder
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public static Object voToProtoBean(Object javaBean, Object protoBuilder) {

        try {
            Method mm = protoBuilder.getClass().getMethod("getDescriptorForType");
            Descriptors.Descriptor descriptor = (Descriptors.Descriptor) mm.invoke(protoBuilder);
            Field[] fields = javaBean.getClass().getDeclaredFields();
            for (Field item : fields) {
                try {
                    String fName = item.getName();
                    item.setAccessible(true);
                    Object jObject = item.get(javaBean);
                    if (null == jObject) {
                        break;
                    }
                    Descriptors.FieldDescriptor fd = descriptor.findFieldByName(fName);
                    if (null != fd) {
                        if (fd.isRepeated()) {
                            boolean isDefined = false;
                            Method[] mmm = protoBuilder.getClass().getMethods();
                            for (Method mItem : mmm) {

                                try {
                                    String mName = mItem.getName();
                                    String mName1 = "add" + firstToUpperCase(fName);
                                    if (mName1.equals(mName) && mItem.getParameterTypes().length == 1) {
                                        Class[] ccList = mItem.getParameterTypes();
                                        Class cc = ccList[0];
                                        Method me = cc.getMethod("newBuilder");
                                        Object oBuilder = me.invoke(null);//获取自定义对象builder

                                        List<Object> dList = (List<Object>) jObject; //数据为List集合
                                        List<Object> pBeanList = new ArrayList<Object>();
                                        for (Object oItem : dList) {
                                            Object pBean = voToProtoBean(oItem, oBuilder);
                                            pBeanList.add(pBean);
                                        }
                                        Method mee = protoBuilder.getClass().getMethod("addAll" + firstToUpperCase(fName), Iterable.class);
                                        mee.invoke(protoBuilder, pBeanList);
                                        isDefined = true;
                                    }
                                } catch (Exception e) {

                                }

                            }

                            if (!isDefined) {
                                try {
                                    Method me = protoBuilder.getClass().getMethod("addAll" + firstToUpperCase(fName), Iterable.class);
                                    me.invoke(protoBuilder, jObject);
                                } catch (Exception e) {
                                    log.info("this repeated field is a user-defined field");
                                    e.printStackTrace();
                                }
                            }


                        } else {
                            boolean isDefined1 = false;
                            try {
                                //自定义对象继续需要通过builder来解析处理，回调
                                Method bM = protoBuilder.getClass().getMethod("getFieldBuilder", Descriptors.FieldDescriptor.class);
                                Object subBuilder = bM.invoke(protoBuilder, fd);
                                Object pBean = voToProtoBean(jObject, subBuilder);
                                Method me = protoBuilder.getClass().getMethod("setField", Descriptors.FieldDescriptor.class, Object.class);
                                me.invoke(protoBuilder, fd, pBean);
                                isDefined1 = true;
                            } catch (Exception e) {
                                log.info("this required field is not a user-defined field");
                            }

                            if (!isDefined1) {
                                Method me = protoBuilder.getClass().getMethod("setField", Descriptors.FieldDescriptor.class, Object.class);
                                me.invoke(protoBuilder, fd, jObject);
                            }

                        }
                    }
                } catch (Exception e) {
                    log.error("javaBeanToProtoBean methoditem reflect error, item name:" + item.getName());
                }
            }

            Method buildM = protoBuilder.getClass().getMethod("build");
            Object rObject = buildM.invoke(protoBuilder);
            return rObject;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("convert javabean to protobuf bean error,e:", e);
            return null;
        }

    }

    //将一个字符串中的首字母转换成大写，其它的全部转换成小写
    public static String firstToUpperCase(String str) {
        String first = str.substring(0, 1);
        String after = str.substring(1); //substring(1),获取索引位置1后面所有剩余的字符串
        first = first.toUpperCase();
        after = after.toLowerCase();
        return first + after;
    }

    public static <T> T byteArrayToVO(byte[] data, Class<T> clz,Descriptors.Descriptor descriptor) throws Exception {
        DynamicMessage dynamicMessage = DynamicMessage.parseFrom(descriptor, data);
        Map<String, Object> maps = new HashMap<>();
        Map<Descriptors.FieldDescriptor, Object> allFields = dynamicMessage.getAllFields();
        for (Map.Entry<Descriptors.FieldDescriptor, Object> entry : allFields.entrySet()) {
            Descriptors.FieldDescriptor key = entry.getKey();
            maps.put(key.getJsonName(), entry.getValue());
        }
        T t = clz.newInstance();

        Field[] fields = clz.getDeclaredFields();
        for (Field field : fields) {
            int mod = field.getModifiers();
            if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
                continue;
            }
            field.setAccessible(true);
            field.set(t, maps.get(field.getName()));
        }


        // BeanUtils.populate(t, maps);
        return t;
    }


}
